// Copyright (c) 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// unique_ptr-style pointer that stores values that may be from an arena. Takes
// up the same storage as the platform's native pointer type. Takes ownership
// of the value it's constructed with; if holding a value in an arena, and the
// type has a non-trivial destructor, the arena must outlive the
// QuicArenaScopedPtr. Does not support array overloads.

#ifndef NET_QUIC_QUIC_ARENA_SCOPED_PTR_H_
#define NET_QUIC_QUIC_ARENA_SCOPED_PTR_H_

#include <cstdint> // for uintptr_t

#include "base/logging.h"
#include "base/macros.h"
#include "net/quic/quic_utils.h"

namespace net {

template <typename T>
class QuicArenaScopedPtr {
    static_assert(QUIC_ALIGN_OF(T*) > 1,
        "QuicArenaScopedPtr can only store objects that are aligned to "
        "greater than 1 byte.");

public:
    // Constructs an empty QuicArenaScopedPtr.
    QuicArenaScopedPtr();

    // Constructs a QuicArenaScopedPtr referencing the heap-allocated memory
    // provided.
    explicit QuicArenaScopedPtr(T* value);

    template <typename U>
    QuicArenaScopedPtr(QuicArenaScopedPtr<U>&& other); // NOLINT
    template <typename U>
    QuicArenaScopedPtr& operator=(QuicArenaScopedPtr<U>&& other);
    ~QuicArenaScopedPtr();

    // Returns a pointer to the value.
    T* get() const;

    // Returns a reference to the value.
    T& operator*() const;

    // Returns a pointer to the value.
    T* operator->() const;

    // Swaps the value of this pointer with |other|.
    void swap(QuicArenaScopedPtr& other);

    // Resets the held value to |value|.
    void reset(T* value = nullptr);

    // Returns true if |this| came from an arena. Primarily exposed for testing
    // and assertions.
    bool is_from_arena();

private:
    // Friends with other derived types of QuicArenaScopedPtr, to support the
    // derived-types case.
    template <typename U>
    friend class QuicArenaScopedPtr;
    // Also befriend all known arenas, only to prevent misuse.
    template <uint32_t ArenaSize>
    friend class QuicOneBlockArena;

    // Tag to denote that a QuicArenaScopedPtr is being explicitly created by an
    // arena.
    enum class ConstructFrom { kHeap,
        kArena };

    // Constructs a QuicArenaScopedPtr with the given representation.
    QuicArenaScopedPtr(void* value, ConstructFrom from);

    // Low-order bits of value_ that determine if the pointer came from an arena.
    static const uintptr_t kFromArenaMask = 0x1;

    // Every platform we care about has at least 4B aligned integers, so store the
    // is_from_arena bit in the least significant bit.
    void* value_;

    DISALLOW_COPY_AND_ASSIGN(QuicArenaScopedPtr);
};

template <typename T>
bool operator==(const QuicArenaScopedPtr<T>& left,
    const QuicArenaScopedPtr<T>& right)
{
    return left.get() == right.get();
}

template <typename T>
bool operator!=(const QuicArenaScopedPtr<T>& left,
    const QuicArenaScopedPtr<T>& right)
{
    return left.get() != right.get();
}

template <typename T>
bool operator==(std::nullptr_t, const QuicArenaScopedPtr<T>& right)
{
    return nullptr == right.get();
}

template <typename T>
bool operator!=(std::nullptr_t, const QuicArenaScopedPtr<T>& right)
{
    return nullptr != right.get();
}

template <typename T>
bool operator==(const QuicArenaScopedPtr<T>& left, std::nullptr_t)
{
    return left.get() == nullptr;
}

template <typename T>
bool operator!=(const QuicArenaScopedPtr<T>& left, std::nullptr_t)
{
    return left.get() != nullptr;
}

template <typename T>
QuicArenaScopedPtr<T>::QuicArenaScopedPtr()
    : value_(nullptr)
{
}

template <typename T>
QuicArenaScopedPtr<T>::QuicArenaScopedPtr(T* value)
    : QuicArenaScopedPtr(value, ConstructFrom::kHeap)
{
}

template <typename T>
template <typename U>
QuicArenaScopedPtr<T>::QuicArenaScopedPtr(QuicArenaScopedPtr<U>&& other)
    : value_(other.value_)
{
    static_assert(
        std::is_base_of<T, U>::value || std::is_same<T, U>::value,
        "Cannot construct QuicArenaScopedPtr; type is not derived or same.");
    other.value_ = nullptr;
}

template <typename T>
template <typename U>
QuicArenaScopedPtr<T>& QuicArenaScopedPtr<T>::operator=(
    QuicArenaScopedPtr<U>&& other)
{
    static_assert(
        std::is_base_of<T, U>::value || std::is_same<T, U>::value,
        "Cannot assign QuicArenaScopedPtr; type is not derived or same.");
    swap(other);
    return *this;
}

template <typename T>
QuicArenaScopedPtr<T>::~QuicArenaScopedPtr()
{
    reset();
}

template <typename T>
T* QuicArenaScopedPtr<T>::get() const
{
    return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(value_) & ~kFromArenaMask);
}

template <typename T>
T& QuicArenaScopedPtr<T>::operator*() const
{
    return *get();
}

template <typename T>
T* QuicArenaScopedPtr<T>::operator->() const
{
    return get();
}

template <typename T>
void QuicArenaScopedPtr<T>::swap(QuicArenaScopedPtr& other)
{
    using std::swap;
    swap(value_, other.value_);
}

template <typename T>
bool QuicArenaScopedPtr<T>::is_from_arena()
{
    return (reinterpret_cast<uintptr_t>(value_) & kFromArenaMask) != 0;
}

template <typename T>
void QuicArenaScopedPtr<T>::reset(T* value)
{
    if (value_ != nullptr) {
        if (is_from_arena()) {
            // Manually invoke the destructor.
            get()->~T();
        } else {
            delete get();
        }
    }
    DCHECK_EQ(0u, reinterpret_cast<uintptr_t>(value) & kFromArenaMask);
    value_ = value;
}

template <typename T>
QuicArenaScopedPtr<T>::QuicArenaScopedPtr(void* value, ConstructFrom from_arena)
    : value_(value)
{
    DCHECK_EQ(0u, reinterpret_cast<uintptr_t>(value_) & kFromArenaMask);
    switch (from_arena) {
    case ConstructFrom::kHeap:
        break;
    case ConstructFrom::kArena:
        value_ = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(value_) | QuicArenaScopedPtr<T>::kFromArenaMask);
        break;
    }
}

} // namespace net

#endif // NET_QUIC_QUIC_ARENA_SCOPED_PTR_H_
